
`include "common_header.verilog"

//  *************************************************************************
//  File : a_fifo_24.vhd
//  *************************************************************************
//  This program is controlled by a written license agreement.
//  Unauthorized reproduction or use is expressly prohibited. 
//  Copyright (c) 2002-2003-2004 Morethanip
//  An der Steinernen Brueke 1, 85757 Karlsfeld, Germany
//  info@morethanip.com
//  http://www.morethanip.com
//  *************************************************************************
//  Designed by : Peter Riedl
//  info@morethanip.com
//  *************************************************************************
//  Decription : Programmable (Asynchronous / Synchronous) FIFO
//  Version    : $Id: a_fifo_24_xgxs.v,v 1.6 2017/06/07 14:37:54 dk Exp $
//  *************************************************************************
// 
//    FIFO Name Coding:
//    ----------------
// 
//       1:      Full
//       2:      Amost Full
//       3       Empty
//       4:      Almost Empty
// 
//  *************************************************************************

module a_fifo_24_xgxs (

   reset_wclk,
   reset_rclk,
   wclk,
  `ifdef USE_CLK_ENA
   wclk_ena,
  `endif     
   wren,
   din,
   sync_wr_rst,
   rclk,
  `ifdef USE_CLK_ENA
   rclk_ena,
  `endif   
   sync_rd_rst,
   sync_rd_ack,
   rden,
   dout,
   afull,
   aempty);

parameter FF_WIDTH      = 3'b 100;
parameter ADDR_WIDTH    = 3'b 111;
parameter DEPTH         = 8'b 10000000;
parameter AF_THRESHOLD  = {ADDR_WIDTH{1'b 0}};
parameter AE_THRESHOLD  = {ADDR_WIDTH{1'b 0}};

input   reset_wclk; 
input   reset_rclk; 
input   wclk; 
input   wren; 
input   [FF_WIDTH - 1:0] din; 
input   sync_wr_rst;
input   rclk; 
input   sync_rd_rst;
output  sync_rd_ack;
input   rden; 
output  [FF_WIDTH - 1:0] dout; 
output  afull; 
output  aempty; 
`ifdef USE_CLK_ENA
input   wclk_ena;
input   rclk_ena;
`endif 

wire    [FF_WIDTH - 1:0] dout; 
wire    afull; 
wire    aempty; 
wire    [ADDR_WIDTH - 1:0] wr_b_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_wptr; 
wire    [ADDR_WIDTH - 1:0] rd_b_wptr; 
reg     [ADDR_WIDTH - 1:0] ptr_wck_diff; 
wire    [ADDR_WIDTH - 1:0] rd_b_ptr; 
wire    [ADDR_WIDTH - 1:0] rd_g_ptr; 
wire    [ADDR_WIDTH - 1:0] wr_g_rptr; 
wire    [ADDR_WIDTH - 1:0] wr_b_rptr; 
reg     [ADDR_WIDTH - 1:0] ptr_rck_diff; 
reg     afull_flag; 
reg     aempty_flag; 
wire    [ADDR_WIDTH - 1:0]  ff_rd_binval; 
reg     sync_rd_ack;
wire    [ADDR_WIDTH - 1:0]  ff_wr_binval;    
wire    sync_rd_rst_reg2;

`define A_FF_24_HEX_MAX  {{(ADDR_WIDTH){1'b 1}}}

mtip_xsync #(1) U_SYRDRST (
        .data_in(sync_rd_rst),
        .reset  (reset_rclk),
        .clk    (rclk),
        .data_s (sync_rd_rst_reg2));

always@(posedge reset_rclk or posedge rclk)
begin

        if (reset_rclk==1'b1)
        begin
        
                sync_rd_ack      <= 1'b0 ;
                
        end
        else
        begin
                        
         `ifdef USE_CLK_ENA
            if(rclk_ena == 1'b 1)
            begin
         `endif                
                
                sync_rd_ack      <= sync_rd_rst_reg2 ;
                
         `ifdef USE_CLK_ENA
            end
         `endif        
        
        end
        
end 

  sdpm_algn_gen #(FF_WIDTH,DEPTH,ADDR_WIDTH) U_RAM_ALIGN (

           `ifdef FF_USE_REGS

           .reset_wclk(reset_wclk),
           .reset_rclk(reset_rclk),

           `endif           

           .wad(wr_b_ptr),
           .din(din),
           .wclk(wclk),
           .rclk(rclk),
           .wren(wren),
           .rad(rd_b_ptr),
           .dout(dout));

gray_cnt_xgxs #(ADDR_WIDTH,DEPTH) U_WRT (

        .clk(wclk),
       `ifdef USE_CLK_ENA
        .clk_ena(wclk_ena),
       `endif          
        .reset(reset_wclk),
        .sync_rst(sync_wr_rst),
        .enable(wren),
        .b_out(wr_b_ptr),
        .g_out(wr_g_ptr));

gray_cnt_xgxs #(ADDR_WIDTH,DEPTH) U_RD (

        .clk(rclk),
       `ifdef USE_CLK_ENA
        .clk_ena(rclk_ena),
       `endif
        .reset(reset_rclk),
        .sync_rst(sync_rd_rst_reg2),
        .enable(rden),
        .b_out(rd_b_ptr),
        .g_out(rd_g_ptr));

// Clock domain crossing from read clock to write clock
// ----------------------------------------------------
mtip_xsync #(ADDR_WIDTH) U_SYRDGPTR (
        .data_in(rd_g_ptr),
        .reset  (reset_wclk),
        .clk    (wclk),
        .data_s (rd_g_wptr));

//  gray to binary conversion
//  -------------------------

assign ff_rd_binval[ADDR_WIDTH-1]   = rd_g_wptr[ADDR_WIDTH-1] ;
assign ff_rd_binval[ADDR_WIDTH-2:0] = gray2bin(rd_g_wptr) ;

assign rd_b_wptr = ff_rd_binval;

//  Asynchronous FIFO, Gray Pointers are Used
//  -----------------------------------------

always @(posedge reset_wclk or posedge wclk)
   begin : ff_aff
   if (reset_wclk == 1'b 1)
      begin
      afull_flag <= 1'b 0;	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(wclk_ena == 1'b 1)
            begin
         `endif        
        
                if (sync_wr_rst==1'b1)
                begin
                
                        afull_flag <= 1'b 0;
                
                end
                else
                begin
              
                        if (ptr_wck_diff >= (`A_FF_24_HEX_MAX - AF_THRESHOLD))
                        begin
                                
                                afull_flag <= 1'b 1;
                                	
                        end
                        else
                        begin
                                
                                afull_flag <= 1'b 0;
                                	
                        end
                end
     
         `ifdef USE_CLK_ENA
            end
         `endif     
     
     end
   end

assign afull = afull_flag; 


// Clock domain crossing

mtip_xsync #(ADDR_WIDTH) U_SYWRGRPTR (
        .data_in(wr_g_ptr),
        .reset  (reset_rclk),
        .clk    (rclk),
        .data_s (wr_g_rptr));

//  convert to binary value

assign ff_wr_binval[ADDR_WIDTH-1]   = wr_g_rptr[ADDR_WIDTH-1] ;
assign ff_wr_binval[ADDR_WIDTH-2:0] = gray2bin(wr_g_rptr) ;

assign wr_b_rptr = ff_wr_binval;

//  Asynchronous FIFO
//  -----------------

always @(posedge reset_rclk or posedge rclk)
   begin : aff_ef
   if (reset_rclk == 1'b 1)
      begin
      aempty_flag <= 1'b 1;	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(rclk_ena == 1'b 1)
            begin
         `endif      
      
              if (ptr_rck_diff < AE_THRESHOLD)
                 begin
                 aempty_flag <= 1'b 1;	
                 end
              else
                 begin                                         
                    if (sync_rd_rst_reg2==1'b1)
                    begin
                
                        aempty_flag <= 1'b 1;
                
                    end
                    else
                    begin
         
                        aempty_flag <= 1'b 0;	
                
                    end
                 end
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
   end

assign aempty = aempty_flag; 
always @(posedge reset_wclk or posedge wclk)
   begin : process_1
   if (reset_wclk == 1'b 1)
      begin
      ptr_wck_diff <= {(ADDR_WIDTH){1'b 0}};	
      end
   else
      begin
      
         `ifdef USE_CLK_ENA
            if(wclk_ena == 1'b 1)
            begin
         `endif        
        
                if (sync_wr_rst==1'b1)
                begin
                
                        ptr_wck_diff <= {(ADDR_WIDTH){1'b 0}};
                
                end
                else
                begin
              
                        ptr_wck_diff <= wr_b_ptr - rd_b_wptr;	
                        
                end
      
         `ifdef USE_CLK_ENA
            end
         `endif      
      
      end
end

always @(wr_b_rptr or rd_b_ptr)
   begin : process_2
   ptr_rck_diff = wr_b_rptr - rd_b_ptr;	
   end

// Gray to Binary Conversion
// -------------------------

function [ADDR_WIDTH-2:0] gray2bin;

        input [ADDR_WIDTH-1:0]  gray_val ;
        
        integer LOOP_IDX1; 
        integer LOOP_IDX2;
                
        for (LOOP_IDX1 = 0; LOOP_IDX1 <= ADDR_WIDTH - 2; LOOP_IDX1 = LOOP_IDX1 + 1)
        begin
         
                gray2bin[LOOP_IDX1] = gray_val[LOOP_IDX1];	

                for (LOOP_IDX2 = ADDR_WIDTH - 1; LOOP_IDX2 >= LOOP_IDX1 + 1; LOOP_IDX2 = LOOP_IDX2 - 1)
                begin
            
                        gray2bin[LOOP_IDX1] = gray2bin[LOOP_IDX1] ^ gray_val[LOOP_IDX2];	
            
                end
                
        end
        
endfunction

endmodule // module a_fifo_24